import time
import os

from common.third_party.devmem import DevMemCache
from common.third_party.save_config import load_const
from services.control.schemas.control_registers import ControlStates, ControlStatesInfo
from settings import settings


class Control:
    __slots__ = ['control_states', 'control_states_info', 'mem']

    def __init__(self) -> None:
        time.sleep(7.2)
        self.mem = DevMemCache()
        self.control_states_info = ControlStatesInfo()

        if not settings.DEBUG:
            states = ControlStates().dict()
            states = dict(map(lambda kv: (kv[0], self.read_state(kv[0])), states.items()))
            self.control_states = ControlStates(**states)
            self.write_state('block_state', load_const('block_type', str, 'FSC').rstrip('\n') == 'FSC')
        else:
            self.control_states = ControlStates()

    def read_state(self, name: str) -> bool:
        info = self.control_states_info.get(name)
        if not settings.DEBUG:
            mem = self.mem.get(info.address)
            return mem[info.offset]
        return self.control_states.get(name)

    def safe_write_state(self, name: str, state: bool, gain_back_brake: bool = False):
        critical_states = ('link_0_state', 'link_1_state', 'service_0_state', 'service_1_state', 'service_2_state', 'service_3_state')
        if name in critical_states and state and not self.read_state(name):
            device_index = critical_states.index(name)
            ad_index = (0, 0, 1, 1, 2, 2)[device_index]
            chanel = (0, 1, 1, 0, 1, 0)[device_index]

            gain = self.read_gain(ad_index, chanel)
            att = self.read_attenuation(ad_index, chanel)
            self.write_gain(ad_index, chanel, 0)
            self.write_attenuation(ad_index, chanel, -70)
            time.sleep(1.3)

            self.write_state(name, state)
            time.sleep(2.0)

            if not gain_back_brake:
                self.write_gain(ad_index, chanel, int(gain))
                self.write_attenuation(ad_index, chanel, int(att))
                time.sleep(0.5)
        else:
            self.write_state(name, state)

    def write_state(self, name: str, state: bool):
        info = self.control_states_info.get(name)
        if not settings.DEBUG:
            mem = self.mem.get(info.address)
            mem[info.offset] = state

        self.control_states.set(name, state)

    def write_gain(self, index: int = 0, channel: int = 0, gain: int = 0):
        fd = os.open(f'/sys/bus/iio/devices/iio:device{index}/in_voltage{channel}_hardwaregain', os.O_RDWR)
        os.write(fd, bytes(str(gain), encoding='utf8'))
        os.close(fd)

    def read_gain(self, index: int = 0, channel: int = 0) -> float:
        with open(f'/sys/bus/iio/devices/iio:device{index}/in_voltage{channel}_hardwaregain', 'r') as gain_file:
            gain = gain_file.read()
            return float(gain.strip()[:-2].strip())

    def write_attenuation(self, index: int = 0, channel: int = 0, attenuation: int = 0):
        fd = os.open(f'/sys/bus/iio/devices/iio:device{index}/out_voltage{channel}_hardwaregain', os.O_RDWR)
        os.write(fd, bytes(str(attenuation), encoding='utf8'))
        os.close(fd)

    def read_attenuation(self, index: int = 0, channel: int = 0) -> float:
        with open(f'/sys/bus/iio/devices/iio:device{index}/out_voltage{channel}_hardwaregain', 'r') as gain_file:
            gain = gain_file.read()
            return float(gain.strip()[:-2].strip())
